package com.yuoj.yuojcodesandbox.sandbox.java;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.yuoj.yuojcodesandbox.CodeSandBox;
import com.yuoj.yuojcodesandbox.model.ExecuteCodeRequest;
import com.yuoj.yuojcodesandbox.model.ExecuteCodeResponse;
import com.yuoj.yuojcodesandbox.model.ExecuteMessage;
import com.yuoj.yuojcodesandbox.model.JudgeInfo;
import com.yuoj.yuojcodesandbox.utils.ProcessUtils;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;

/**
 * @author 刘渠好
 * @since 2025-03-31 22:48
 * java原生代码沙箱升级版本
 */
@Slf4j
public abstract class JavaNativeCodeSandboxPractice implements CodeSandBox {
    //存放代码
    public static final String CODE_TEMP_DIR ="codeTemp";

    //统一类名
    public static final String JAVA_CLASS_NAME ="Main.java";

    //限制时间
    public static final long LIMIT_MAX_TIME=3000L; //3s

    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest request) {
        String code = request.getCode();
        List<String> input = request.getInput();

        //1.保存代码文件
        String itemPath = System.getProperty("user.dir");
        String codeTempPath =itemPath+ File.separator+CODE_TEMP_DIR;
        if (!FileUtil.exist(codeTempPath)) {
            FileUtil.mkdir(codeTempPath);
        }
        //存放代码
        String codeParentPath = codeTempPath+File.separator+ UUID.randomUUID();
        String codePath = codeParentPath+File.separator+JAVA_CLASS_NAME;
        File codeFile = FileUtil.writeString(code, codePath, StandardCharsets.UTF_8);

        //2.编译代码
        String compileCmd = String.format("javac -encoding utf-8 %s", codeFile.getAbsolutePath());
        try {
            Process comProcess = Runtime.getRuntime().exec(compileCmd);
            ExecuteMessage executeMessage = ProcessUtils.runProcessAndGetMessage(comProcess, "编译");
            if (executeMessage.getExitValue()!=0){
                log.error("{}",executeMessage.getErrorMessage());
                return ExecuteCodeResponse.builder()
                        .status(2)
                        .message("编译失败!")
                        .build();
            }
        } catch (IOException e) {
            FileUtil.del(codeParentPath);
            return getErrorResponse(e);
        }

        //3.执行代码并且收集执行结果

        List<String> outPutList = new LinkedList<> ();

        long maxTime=0;

        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        executeCodeResponse.setStatus(1);

        for (String args : input) {
            String runCmd = String.format("java -Dfile.encoding=utf-8 -cp %s Main %s", codeParentPath, args);
            try {
                Process runProcess = Runtime.getRuntime().exec(runCmd);
                //时间限制
                new Thread(){
                    public void run() {
                        try {
                            Thread.sleep(LIMIT_MAX_TIME);
                            if (runProcess.isAlive()) {
                                runProcess.destroy();
                                log.info("超时了！");
                            }
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }

                }.start();
                ExecuteMessage executeMessage = ProcessUtils.runProcessAndGetMessage(runProcess, "执行");
                if (StrUtil.isNotBlank(executeMessage.getErrorMessage())) {
                    //如果第一次执行无错误，第二次执行有错误就把集合清空
                    outPutList=new LinkedList<>();
                    log.error(executeMessage.getErrorMessage());
                    executeCodeResponse.setStatus(2);
                    executeCodeResponse.setMessage(executeMessage.getErrorMessage());
                    break;
                }
                outPutList.add(executeMessage.getMessage());
                maxTime=Math.max(maxTime,executeMessage.getTime());
            } catch (IOException e) {
                //执行失败了，也把文件删掉
                FileUtil.del(codeParentPath);
                return getErrorResponse(e);
            }

        }

        //4.整理执行结果
        executeCodeResponse.setOutput(outPutList);
        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setTime(maxTime);
        executeCodeResponse.setJudgeInfo(judgeInfo);

        //5.清理文件
        if (FileUtil.exist(codeFile.getParentFile().getAbsolutePath())) {
            boolean del = FileUtil.del(codeParentPath);
            log.info("删除文件{}",del? "成功":"失败");
        }


        return executeCodeResponse;
    }

    //6.增强程序健壮性
    public ExecuteCodeResponse getErrorResponse(Throwable e){
        return ExecuteCodeResponse.builder()
                .status(3)
                .judgeInfo(new JudgeInfo())
                .output(new LinkedList<>())
                .message(e.getMessage())
                .build();
    }
}

